#include "threadpool.h"
#include <functional>
#include <glog/logging.h>
#include <memory>

ThreadPool::ThreadPool(int nums)
    : status_(POOL_STATUS_UNINIT), threadnum_(nums), lasteq(0) {
  LOG(INFO) << "Thread Pool Inited Instance" << this;
}

ThreadPool::~ThreadPool() {}

void NormalPoolNotifierAfter(std::shared_ptr<ThreadPool> own, int id, int cmd) {
  LOG(INFO) << "Thread: " << id << " Return Free";
  own->Notify(id, cmd);
}

int ThreadPool::PostTask(FuncType func, std::shared_ptr<void> args) {
  std::lock_guard<std::mutex> lock(this->threadmutex_);
  if (freethreadset_.begin() != freethreadset_.end()) {
    int id = freethreadset_.begin()->first;
    auto q = freethreadset_.begin()->second;
    if (!q.expired()) {
      auto qtemp = q.lock();
      LOG(INFO) << " PostTask " << qtemp.get();
      qtemp->post(func, args);
      // 移动到忙表中
      freethreadset_.erase(id);
      workthreadset_.insert(std::make_pair(id, q));
    }
    return 0;
  }
  return -1;
}

int ThreadPool::Fini() {
  for (auto i = threads_.begin(); i != threads_.end(); ++i) {
    i->second->stop();
    i->second->Uinit();
    threads_.erase(i);
  }
  return 0;
}

int ThreadPool::MoveThread2Free(int id) {
  std::lock_guard<std::mutex> lock(this->threadmutex_);
  std::weak_ptr<Thread> pw;
  if (threads_.find(id) == threads_.end()) {
    return -1;
  }
  if (this->workthreadset_.find(id) != this->workthreadset_.end()) {
    pw = workthreadset_[id];
    workthreadset_.erase(id);
  }
  if (!pw.expired()) {
    freethreadset_.insert(std::make_pair(id, pw));
  }
  return 0;
}

int ThreadPool::MoveThread2Work(int id) {
  std::lock_guard<std::mutex> lock(this->threadmutex_);
  std::weak_ptr<Thread> pw;
  if (freethreadset_.find(id) == freethreadset_.end() &&
      threads_.find(id) == threads_.end()) {
    return -1;
  }
  if (this->freethreadset_.find(id) != this->freethreadset_.end()) {
    pw = freethreadset_[id];
    freethreadset_.erase(id);
  }
  if (!pw.expired()) {
    workthreadset_.insert(std::make_pair(id, pw));
  }
  return 0;
}

int ThreadPool::Notify(int id, int cmd) {
  // 在结束时停止回调，防止死锁无法join
  if (status_.load() != POOL_STATUS_RUN) {
    return 0;
  }
  if (cmd == 2) {
    MoveThread2Free(id);
  }
  return 0;
}

int ThreadPool::Init() {
  ThreadNotifyer fier(new Thread_notifyer);
  fier->pstart = nullptr;
  fier->pstop = std::bind(NormalPoolNotifierAfter, shared_from_this(),
                          std::placeholders::_1, std::placeholders::_2);
  for (int i = 0; i < threadnum_; ++i) {
    std::shared_ptr<Thread> pthreads(new Thread(lasteq, fier));
    // 启动线程
    pthreads->Init();
    LOG(INFO) << "ThreadPool" << "Insert Thread " << pthreads.get();
    threads_.insert(std::make_pair(lasteq, pthreads));
    freethreadset_.insert(std::make_pair(lasteq, pthreads));
    ++lasteq;
  }
  status_.store(POOL_STATUS_RUN);
  return 0;
}
